
local raw = ...
local raw_resourcemeta = raw.resourcemeta
local raw_loadpng = raw.loadpng
local raw_loadbmp = raw.loadbmp
local raw_loadjpeg = raw.loadjpeg
local raw_loadgif = raw.loadgif
local raw_window = raw.window
local raw_draw = raw.draw
local raw_image_create = raw.image_create

local io_open = io.open
local getmetatable, setmetatable = raw.getmetatable, raw.setmetatable
local pcall = pcall
local getfenv,setfenv = raw.getfenv,raw.setfenv
local error = error
local pcall = pcall

local function constant(value)
	return function()  return value;  end
end
local function method(func,meta,metaname)
	return constant(function(self, ...)
		if getmetatable(self) ~= meta then
			error('invalid "self" parameter - you should call ' .. metaname .. ':method(...), not ' .. metaname .. '.method(...)', 3)
		end
		return func(self, ...)
	end)
end

-- Maps [image window -> original image]; keeps original from being garbage collected if the window is still alive, but not vice-versa
local window_gc_handler = setmetatable({}, {__mode = 'k'})

local image_meta do
	image_meta = {
		__gc = raw.destroyimage;
		__tostring = raw.imagesummary;
	}
	
	local function image_window(self, ...)
		local new_window = raw_window(self, ...)
		window_gc_handler[new_window] = self
		return setmetatable(new_window, image_meta)
	end

	local image_getters = {
		width = raw.imagegetwidth;
		height = raw.imagegetheight;
		bpp = raw.imagegetbpp;
		type = raw.imagegettype;
		rgba = method(raw.imagergba, image_meta, 'image');
		colcode = method(raw.imagecolcode, image_meta, 'image');
		window = method(image_window, image_meta, 'image');
		clear = method(raw.image_clear, image_meta, 'image');
		destroy = method(raw.destroyimage, image_meta, 'image');
		mask = raw.getmask;
		gethandle = method(raw.gethandle, image_meta, 'image');
		sethandle = method(raw.sethandle, image_meta, 'image');
		lock = method(raw.lock, image_meta);
	}
	image_getters.rgb = image_getters.rgba

	local image_setters = {
		mask = raw.setmask;
	}

	function image_meta:__index(key)
		local getter = image_getters[key]
		if getter then
			return getter(self)
		end
		local extravars = getfenv(self)
		if extravars then
			return extravars[key]
		end
		return nil
	end;
	function image_meta:__newindex(key, value)
		local setter = image_setters[key]
		if setter then
			return setter(self, value)
		end
		if image_getters[key] then
			error('attempt to set read-only field "' .. key .. '"', 2)
		end
		local extravars = getfenv(self)
		if not extravars then
			extravars = {}
			setfenv(self, extravars)
		end
		extravars[key] = value
	end;
end

raw.registry["image meta"] = image_meta

local image_resource_cache = setmetatable({}, {__mode = 'kv'});

return {

	resource = function(f)
		--[[
		if getmetatable(f) ~= raw_resourcemeta then
			error('expecting resource object', 2)
		end
		--]]
		if (f == nil) then
			error('expecting resource object', 2)
		end
		local cached = image_resource_cache[f];
		if cached then
			return cached;
		end
		local scriptf;
		if f.name:lower():match("%.lua$") then
			scriptf = f;
		else
			scriptf = f:sibling(f.name .. ".lua");
		end
		local img;
		if scriptf then
			img = runscript(scriptf);
		else
			img = image.load(f);
		end
		img:lock();
		image_resource_cache[f] = img;
		return img;
	end;
	
	load = function(f)
		local path, offset, length = f.path, f.offset or 0, f.length
		local fp = io_open(path, 'rb')
		fp:seek('set', offset)
		
		local header = fp:read(10) or ''
		
		fp:seek('set', offset)
		
		if header:match("^\137PNG\13\10\26\10") then
			local success, result = pcall(raw_loadpng, fp)
			fp:close()
			if not success then
				return nil, result
			end
			return setmetatable(result, image_meta)
		elseif header:match("^GIF8[97]a") then
			fp:close()
			return nil, 'GIF not yet supported'
		elseif header:match("^\255\216\255\224..JFIF") then
			local success, result = pcall(raw_loadjpeg, fp)
			fp:close()
			if not success then
				return nil, result
			end
			return setmetatable(result, image_meta)
		elseif header:match("^BM") then
			local success, result = pcall(raw_loadbmp, fp)
			fp:close()
			if not success then
				return nil, result
			end
			return setmetatable(result, image_meta)
		end
		
		fp:close()
		return nil, 'unrecognised image format'
	end;
	
	draw = function(im, ...)
		if getmetatable(im) ~= image_meta then
			error('invalid image parameter', 2)
		end
		return raw_draw(im, ...)
	end;
	
	create = function(...)
		local new_im = raw_image_create(...)
		return setmetatable(new_im, image_meta)
	end;
	
}
